package com.health.es.temp;

import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.LRUCache;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.health.es.enums.ColumnEnum;
import com.health.es.service.DictService;
import lombok.SneakyThrows;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 字典转换工具类
 *
 * @author nqlz
 */
@Component
public class DictionaryUtil {
    public static Cache<String, Map<String, String>> DICT  = CacheUtil.newLFUCache(145, DateUnit.DAY.getMillis());
    public static Cache<String, String> HOSP = CacheUtil.newLFUCache(145, DateUnit.DAY.getMillis());
    public static final Cache<Class<?>, Set<String>> CLASS_SET_MAP = new LRUCache<>(160);
    public static DictService dictService;

    public static ObjectMapper objectMapper;


    public DictionaryUtil(DictService dictService, ObjectMapper objectMapper) {
        DictionaryUtil.objectMapper = objectMapper;
        DictionaryUtil.dictService = dictService;
    }

    /**
     * 转换后对象类型不变
     *
     * @param obj
     * @param <T>
     * @return
     */
    @SneakyThrows
    public static <T> T convert(T obj) {
        return objectMapper.treeToValue(convertJson(obj), (ClassUtil.getClass(obj)));
    }

    /**
     * 使用 Jackson 来实现 json 转换，保证序列化准确
     *
     * @param obj
     * @param <T>
     * @return
     */
    public static <T> ObjectNode convertJson(T obj) {
        return resolveJson(objectMapper.valueToTree(obj), null);
    }

    public static <T> T convertFastJson(T obj) {
        return resolveJson(obj, null).toJavaObject(ClassUtil.getClass(obj));
    }

    /**
     * 转换 对象指定字段，字符串
     *
     * @param obj
     * @param fields
     * @param <T>
     * @return
     */
    public static <T> ObjectNode convertJson(T obj, String... fields) {
        Function<String, Boolean> filterB = fieldName -> ColumnEnum.containsKey(fieldName) && ArrayUtil.contains(fields, fieldName);
        return resolveJson(objectMapper.valueToTree(obj), filterB);
    }

    private static ObjectNode resolveJson(ObjectNode obj, Function<String, Boolean> filterB) {
        HashSet<String> keySet = CollUtil.newHashSet(false, obj.fieldNames());

        Set<String> fieldNames = null == filterB ? CLASS_SET_MAP.get(obj.getClass(), () -> keySet.stream()
                .filter(ColumnEnum::containsKey).collect(Collectors.toSet())) :
                keySet.stream().filter(filterB::apply).collect(Collectors.toSet());

        if (CollUtil.isNotEmpty(fieldNames)) {
            fieldNames.forEach(fieldName -> {
                String fieldValue;
                if (Objects.isNull(obj.get(fieldName)) || StrUtil.isBlank(fieldValue = obj.get(fieldName).asText())) {
                    obj.put(fieldName + "Name", "");
                    return;
                }
                if (!DICT.containsKey(fieldName)) {
                    Optional<ColumnEnum> first = Optional.ofNullable(ColumnEnum.get(fieldName));
                    obj.put(fieldName + "Name", first.isPresent() ? first.get().getDictName(fieldValue) : "");
                    return;
                }
                obj.put(fieldName + "Name", DICT.get(fieldName).get(fieldValue));
            });
        }
        return obj;
    }

    public static <T> JSONObject resolveJson(Object object, Function<String, Boolean> filterB) {
        return resolveJson(object, object.getClass(), filterB);
    }

    public static <T> JSONObject resolveJson(Object object, Class<T> tClass, Function<String, Boolean> filterB) {
        JSONObject json = (JSONObject) JSONObject.toJSON(object);
        Supplier<Set<String>> keySet = () -> Arrays.stream(tClass.getDeclaredFields()).map(Field::getName).collect(Collectors.toSet());
        Set<String> fieldNames = null == filterB ? CLASS_SET_MAP.get(tClass, () -> keySet.get().stream()
                .filter(ColumnEnum::containsKey).collect(Collectors.toSet())) :
                keySet.get().stream().filter(filterB::apply).collect(Collectors.toSet());
        putValue(json, fieldNames);
        return json;
    }

    private static void putValue(JSONObject json, Set<String> fieldNames) {
        if (CollUtil.isNotEmpty(fieldNames)) {
            fieldNames.forEach(fieldName -> {
                String fieldValue;
                if (Objects.isNull(json.get(fieldName)) || StrUtil.isBlank(fieldValue = json.getString(fieldName))) {
                    json.put(fieldName + "Name", "");
                    return;
                }
                //有 key size为0  或无 key 则查询
                boolean b = (DICT.containsKey(fieldName)
                        && DICT.get(fieldName).size() < 1)
                        || !DICT.containsKey(fieldName);
                if (b) {
                    Optional<ColumnEnum> first = Optional.ofNullable(ColumnEnum.get(fieldName));
                    json.put(fieldName + "Name", first.isPresent() ? first.get().getDictName(fieldValue) : "");
                    return;
                }
                json.put(fieldName + "Name", DICT.get(fieldName).get(fieldValue));
            });
        }
    }


    /**
     * 转换为 R 对象
     *
     * @param obj
     * @param rClass
     * @param after
     * @param <T>
     * @param <R>
     * @return
     */
    @SneakyThrows
    public static <T, R> R convert(T obj, Class<R> rClass, Consumer<R> after) {
        R r = objectMapper.treeToValue(convertJson(obj), rClass);
        after.accept(r);
        return r;
    }

    @SneakyThrows
    public static <T, R> R convert(T obj, Class<R> rClass) {
        return objectMapper.treeToValue(convertJson(obj), rClass);
    }


    /**
     * 转换 对象指定字段，lambda 表达式
     *
     * @param obj
     * @param function
     * @param <T>
     * @return
     */
    @SafeVarargs
    public static <T> ObjectNode convertJson(T obj, Func1<T, ?>... function) {
        String column = QueryWrapper.columnToString(function);
        return convertJson(obj, column.split(StrPool.COMMA));
    }

}
